//=============================================================================
// IMPLEMENTACIO DE LA CLASSE 'DIRECTORYMANAGER'
//=============================================================================
// per Marc Delgado i Alex Tudorache (marcdelgado@gmail.com) | rev. 2012.06.11

#include "directorymanager.hpp"


// FUNCIONS PUBLIQUES
///////////////////////////////////////////////////////////////////////////////


directoryManager::directoryManager(char* disk)
{
    _buffman = new bufferManager (disk);
    _pagCtrl = new char[1024];
    _pagCtrl = _buffman -> query(0);
    // Mirem si hi ha contingut al disc
    byte b = _pagCtrl[0];
    if (b.getBit(0) == 0) {
        //cout << "INFO: Disc nou." << endl;

        setUsedPag(0);
        newControl();
        //cout << "INFO: S'ha creat l'espai de control. " << endl;

    } else {
        //cout << "INFO: Disc amb contingut." << endl;
    }
}


directoryManager::~directoryManager()
{
    delete(_buffman);
}


void directoryManager::close()
{
    _buffman -> update(0);
}


bool directoryManager::make(const string &filename)
{
    properties prp;
    make(filename, NULL, NULL, NULL);
    return true;
}


bool directoryManager::make(const string &fileName, const int &tf, const int &mr, const int &mcc)
{
    unsigned short fs = getFreeSlot();
    int fp = getFreePag();

    if (fs == 0 || fp == -1){
        //cout <<  "ERROR MAKE(" << fileName << "): Directory o disc plens." << endl;
        return false;
    }
    else
    {
        slot s;
        s = getSlot(fileName);
        if (s.fileName != ""){
            //cout <<  "ERROR MAKE(" << fileName << "): L'arxiu ja existeix." << endl;
            return false;
        }
        else
        {
            s.fileName = fileName;
            s.firstPag = fp;
            s.slotNum = fs;
            s.tf = tf;
            s.mr = mr;
            s.mcc = mcc;
            setSlot(s);
            setFreeSlot(fs + 12);
            setUsedPag(fp);
            //cout << "MAKE(" << fileName << "): REG=" << fs  << " PAG=" << fp << endl;
            return true;
        }
    }
}




bool directoryManager::del(const string &fileName)
{
    slot s = getSlot(fileName);
    if (s.fileName == ""){
        //cout << "ERROR DELETE(" << fileName << "): L'arxiu no existeix." << endl;
        return false;
    }
    else
    {
        //cout << "DELETE(" << fileName << "):";
        unsigned short pagTmp, pagVal = s.firstPag;
        pagTmp = pagVal;
        while(getNextPagVal(pagVal) != 0) {
            pagTmp = getNextPagVal(pagVal);
            setNextPagVal(pagVal, 0);
            setFreePag(pagVal);
            //cout << " " << pagVal;
            pagVal = pagTmp;
        }

        if (pagTmp != 0) {
            setNextPagVal(pagTmp, 0);
            setFreePag(pagTmp);
            //cout << " " << pagTmp;
        }
        deleteSlot(s.slotNum);
        //cout << endl;
        return true;
    }

}


unsigned short directoryManager::addpag(const string &fileName)
{
    slot s = getSlot(fileName);
    unsigned short fp = -1;
    if (s.fileName != "")
    {
        fp = getFreePag();
        if (fp == -1) {
            //cout << "ERROR ADDPAG(" << fileName << "): Disc ple." << endl;
            return false;
        } else {
            //cout << "ADDPAG(" << fileName << "):";
            unsigned short pagVal = s.firstPag;
            while(getNextPagVal(pagVal) != 0) {
                //cout << " " << pagVal;
                pagVal = getNextPagVal(pagVal);
            }

            //cout << " " << pagVal;
            setNextPagVal(pagVal, fp);
            setUsedPag(fp);
            //cout << " (" << fp << ")" << endl;
        }
    }
    return fp;
}


bool directoryManager::delpag(const string &fileName)
{
    slot s = getSlot(fileName);
    if (s.fileName == ""){
        //cout <<  "ERROR LIST(" << fileName << "): L'arxiu no existeix." << endl;
        return false;
    }
    else
    {
        unsigned short pagAnt, pagVal = s.firstPag;
        if (getNextPagVal(pagVal) == 0) {
            //cout << "ERROR DELPAG(" << fileName << "): Arxiu amb una sola pagina." << endl;
            return false;
        } else {
            //cout << "DELPAG(" << fileName << "):";
            while(getNextPagVal(pagVal) != 0) {
                pagAnt = pagVal;
                pagVal = getNextPagVal(pagVal);
            }

            setNextPagVal(pagAnt, 0);
            setFreePag(pagVal);
            //cout << " (" << pagVal << ")" << endl;
            return true;
        }
    }
}


llistaPags directoryManager::list(const string &fileName)
{
    llistaPags lp;
    if (fileName == "#map") {
        //cout << "LIST(#map): " << endl;
    } else if (fileName == "#all") {

        //cout << "LIST(#all): " << endl;
        unsigned short sl;
        for (sl = 520; sl <= 1012; sl += 12) {
            slot s = getSlot(sl);
            if (s.fileName != "") list(s.fileName);
        }
    } else {
        slot s = getSlot(fileName);
        if (s.fileName == ""){

            //cout <<  "ERROR LIST(" << fileName << "): L'arxiu no existeix." << endl;
        }
        else
        {
            //cout << "LIST(" << fileName << "):";
            unsigned short pagVal = s.firstPag;
            while(getNextPagVal(pagVal) != 0) {
                //cout << " " << pagVal;
                lp.push_front(pagVal);
                pagVal = getNextPagVal(pagVal);
            }
            //cout << " " << pagVal << endl;
            lp.push_front(pagVal);
        }
    }
    return lp;
}


properties directoryManager::getProperties(const string &fileName)
{
    slot sl = getSlot(fileName);
    properties prp(sl.tf,sl.mr,sl.mcc);
    return prp;
}


char* directoryManager::readPage(const int pPag)
{
    return _buffman->query(pPag);
}


void directoryManager::writePage(const int pPag)
{
    _buffman->update(pPag);
}


// FUNCIONS PRIVADES
///////////////////////////////////////////////////////////////////////////////


int directoryManager::getFreePag()
{
    bool found = false;
    int i,x,n;

	for (i = 0; i < 512 && !found; i++)
	{
		byte val = _pagCtrl[i];

		// Revisem cada bit
		for (x = 0; x < 8 && !found; x++)
		{
		    n = i*8+x;
		    found = (val.getBit(x) == 0);
		}
	}

    return found ? n : -1;
}


void directoryManager::setFreePag(const int &n)
{
    _pagCtrl[n/8] &= ~(1 << n%8);
    char* myStr = _buffman->query(n);
    for(int i = 0; i < _sPage; i++) myStr[i] = 0;
    _buffman->update(n);
}


void directoryManager::setUsedPag(const int &n)
{
    _pagCtrl[n/8] |=  (1 << n%8);
}


bool directoryManager::isPagInUse(const int &n)
{
    byte val = _pagCtrl[n/8];
    return (val.getBit(n%8) != 0);
}


directoryManager::slot directoryManager::getSlot(const int &n)
{
    slot s;
    if (validSlot(n)) {
        unsigned char* bytes = (unsigned char *)&_pagCtrl[n];
        s.fileName = reinterpret_cast<char const*>(bytes);
        s.firstPag = (int) (_pagCtrl[n+6] & 0XFF) << 8;
        s.firstPag += (int) _pagCtrl[n+6+1] & 0XFF;
        s.tf = _pagCtrl[n+6+2];
        s.mr = _pagCtrl[n+6+3];
        s.mcc = _pagCtrl[n+6+4];
        s.slotNum = n;
    }
    return s;
}


directoryManager::slot directoryManager::getSlot(const string &fileName)
{
    slot ret;
    bool found = false;
    unsigned short sl;
    for (sl = 520; sl <= 1012 && !found; sl += 12) {
        slot s = getSlot(sl);
        if (s.fileName == fileName) {
            ret = s;
            found = true;
        }
    }
    return ret;
}


void directoryManager::setSlot(const slot &s)
{
    // Si estem dins del rang i el registre no s'ha utilitzat
    if (s.slotNum >= 520 && s.slotNum < 1024 && !validSlot(s.slotNum)) {
    unsigned char *bytes = (unsigned char *)&_pagCtrl[s.slotNum];

    // Escrivim el nom
    unsigned int i;
    for (i = 0; i < 6; i++){
        bytes[i] = s.fileName[i];
    }

    // Primera pagina
	_pagCtrl[s.slotNum+6] = (char) ((s.firstPag>>8) & 0XFF);
	_pagCtrl[s.slotNum+6+1] = (char) ((s.firstPag) & 0XFF);
	_pagCtrl[s.slotNum+6+2] = s.tf;
	_pagCtrl[s.slotNum+6+3] = s.mr;
	_pagCtrl[s.slotNum+6+4] = s.mcc;

    } else {
        //cout <<  "ERROR SETSLOT( " << s.fileName << "): No s'ha pogut afegir el registre al disc." << endl;
    }
}


bool directoryManager::validSlot(const int &n)
{
    int i = 0;
    if (n >= 520 && n < 1024) {
        i = (int) (_pagCtrl[n+6] & 0XFF) << 8;
        i += (int) _pagCtrl[n+6+1] & 0XFF;
    }
    return (i != 0);
}


void directoryManager::setNextPagVal(const int &currPag, const unsigned short &nextPag)
{
    char* pag = _buffman -> query(currPag);
	pag[0] = (char) ((nextPag>>8) & 0XFF);
	pag[1] = (char) ((nextPag) & 0XFF);
	_buffman -> update(currPag);
}


unsigned short directoryManager::getNextPagVal(const int &currPag)
{
    char* pag = _buffman -> query(currPag);
    unsigned short i = (int) (pag[0] & 0XFF) << 8;
    i += (int) pag[1] & 0XFF;
    _buffman->unpin(i);
    return i;
}


void directoryManager::newControl()
{
    writeUShort(520, 512, 513);
}

unsigned short directoryManager::getFreeSlot()
{
    unsigned short nfs = readUShort(512, 513);
    if (nfs < 1024)
        return nfs;
    else
        return 0;
}


void directoryManager::setFreeSlot(const int &n)
{
    writeUShort(n, 512, 513);
}


void directoryManager::deleteSlot(const int &n)
{
    int x, i;
    //for (i = n; i < n + 6 + sizeof(unsigned short); i++)
    for (i = n; i < n + 12; i++)
    {
	    byte val = _pagCtrl[i];
	    for (x = 0; x < 8; x++)
	        _pagCtrl[i] &= ~(1 << x);
    }
}


unsigned short directoryManager::readUShort(const int &pos1, const int &pos2)
{
    unsigned short i = (int) (_pagCtrl[pos1] & 0XFF) << 8;
    i += (int) _pagCtrl[pos2] & 0XFF;
    return i;
}


void directoryManager::writeUShort(const unsigned short &valor, const int &pos1, const int &pos2)
{
	_pagCtrl[pos1] = (char) ((valor>>8) & 0XFF);
	_pagCtrl[pos2] = (char) ((valor) & 0XFF);
}


